/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "movement_napi.h"

#include "movement_common.h"
#include "movement_client.h"

using namespace OHOS::Msdp;
using namespace OHOS;
namespace {
auto &g_MovementClient = MovementClient::GetInstance();
static napi_ref g_responseConstructor;
static const uint8_t ARG_0 = 0;
static const uint8_t ARG_1 = 1;
static const uint8_t ARG_2 = 2;
static const std::vector<std::string> vecMovementValue {
    "VALUE_ENTER", "VALUE_EXIT"
};
}
std::map<int32_t, sptr<ImovementCallback>> MovementNapi::callbackMap_;
std::map<int32_t, MovementNapi*> MovementNapi::objectMap_;
napi_ref MovementNapi::movementValueRef_;

struct ResponseEntity {
    MovementDataUtils::MovementValue value;
};

void MovementCallback::OnMovementChanged(const MovementDataUtils::MovementData& movementData)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Callback enter");
    MovementNapi* movementNapi = MovementNapi::GetMovementNapi(movementData.type);
    if (movementNapi == nullptr) {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "movementNapi is nullptr");
        return;
    }

    movementNapi->OnMovementChangedDone(static_cast<int32_t> (movementData.type),
        static_cast<int32_t> (movementData.value), false);

    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Callback exit");
}

MovementNapi* MovementNapi::GetMovementNapi(int32_t type)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter, type = %{public}d", type);

    MovementNapi* obj = nullptr;
    bool isExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isExists = true;
            obj = (MovementNapi*)(it->second);
            MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Found object");
            break;
        }
    }
    if (!isExists) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "Didn't find object");
    }
    return obj;
}

MovementNapi::MovementNapi(napi_env env, napi_value thisVar) : MovementEvent(env, thisVar)
{
    env_ = env;
    callbackRef_ = nullptr;
    movementValueRef_ = nullptr;
}

MovementNapi::~MovementNapi()
{
    if (callbackRef_ != nullptr) {
        napi_delete_reference(env_, callbackRef_);
    }

    if (movementValueRef_ != nullptr) {
        napi_delete_reference(env_, movementValueRef_);
    }
}

napi_value MovementNapi::CreateInstanceForResponse(napi_env env, int32_t value)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value cons = nullptr;
    napi_value instance = nullptr;
    napi_status callBackStatus;
    ResponseEntity *entity = nullptr;

    callBackStatus = napi_get_reference_value(env, g_responseConstructor, &cons);
    if (callBackStatus != napi_ok) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "napi get reference value failed");
        return nullptr;
    }
    callBackStatus = napi_new_instance(env, cons, 0, nullptr, &instance);
    if (callBackStatus != napi_ok || instance == nullptr) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "napi new reference failed");
        return nullptr;
    }
    callBackStatus = napi_unwrap(env, instance, (void **)&entity);
    if (callBackStatus != napi_ok || entity == nullptr) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "%{public}s: cannot unwrap entity from instance", __func__);
        return nullptr;
    }
    entity->value = MovementDataUtils::MovementValue(value);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");

    return instance;
}

void MovementNapi::OnMovementChangedDone(const int32_t& type, const int32_t& value, bool isOnce)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter, value = %{public}d", value);
    OnEvent(type, ARG_1, value, isOnce);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
}

void MovementNapi::InvokeCallBack(napi_env env, napi_value *args, int32_t value)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");

    napi_value callback = nullptr;
    napi_value indexObj = nullptr;
    napi_create_object(env, &indexObj);
    napi_value successIndex = nullptr;

    napi_ref callbackSuccess = nullptr;
    napi_value ret;

    bool flag = value == 0 ? false : true;
    napi_get_boolean(env, flag, &successIndex);
    
    napi_set_named_property(env, indexObj, "movementValue", successIndex);
    napi_create_reference(env, args[ARG_1], 1, &callbackSuccess);
    napi_get_reference_value(env, callbackSuccess, &callback);
    napi_call_function(env, nullptr, callback, 1, &indexObj, &ret);
    napi_delete_reference(env, callbackSuccess);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
}

int32_t MovementNapi::ConvertTypeToInt(std::string type)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    if (type == "inVehicle") {
        return MovementDataUtils::MovementType::TYPE_IN_VEHICLE;
    } else if (type == "inAuto") {
        return MovementDataUtils::MovementType::TYPE_IN_AUTO;
    } else if (type == "inPlane") {
        return MovementDataUtils::MovementType::TYPE_IN_PLANE;
    } else if (type == "inMetro") {
        return MovementDataUtils::MovementType::TYPE_IN_METRO;
    } else if (type == "inElevator") {
        return MovementDataUtils::MovementType::TYPE_IN_ELEVATOR;
    } else if (type == "inBicycle") {
        return MovementDataUtils::MovementType::TYPE_ON_BICYCLE;
    } else if (type == "stay") {
        return MovementDataUtils::MovementType::TYPE_STAY;
    } else if (type == "still") {
        return MovementDataUtils::MovementType::TYPE_STILL;
    } else if (type == "relativeStill") {
        return MovementDataUtils::MovementType::TYPE_RELATIVE_STILL;
    } else if (type == "walking") {
        return MovementDataUtils::MovementType::TYPE_WALKING;
    } else if (type == "walkingHandHold") {
        return MovementDataUtils::MovementType::TYPE_WALKING_HANDHOLD;
    } else if (type == "fastWalking") {
        return MovementDataUtils::MovementType::TYPE_FAST_WALKING;
    } else if (type == "running") {
        return MovementDataUtils::MovementType::TYPE_RUNNING;
    } else if (type == "lyingPosture") {
        return MovementDataUtils::MovementType::TYPE_LYING_POSTURE;
    } else if (type == "highSpeedRail") {
        return MovementDataUtils::MovementType::TYPE_HIGH_SPEED_RAIL;
    } else {
        return MovementDataUtils::MovementType::TYPE_INVALID;
    }
}

napi_value MovementNapi::SubscribeMovement(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    NAPI_ASSERT(env, valueType1 == napi_string, "type mismatch for parameter 1");

    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    NAPI_ASSERT(env, valueType2 == napi_function, "type mismatch for parameter 2");

    size_t len;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    std::vector<char> typeBuf(len + 1);
    status = napi_get_value_string_utf8(env, args[0], typeBuf.data(), len + 1, &len);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "napi_get_value_string_utf8");

    if (status != napi_ok) {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Failed to get string item");
        return result;
    }

    int32_t type = ConvertTypeToInt(typeBuf.data());
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "type is %{public}d", type);

    NAPI_ASSERT(env, type >=  MovementDataUtils::MovementType::TYPE_IN_VEHICLE && type <=
        MovementDataUtils::MovementType::TYPE_HIGH_SPEED_RAIL, "type is illegal");

    MovementNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "Object already exists");
            obj = (MovementNapi*)it->second;
            break;
        }
    }
    if (!isObjExists) {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Didn't find object, so created it");
        obj = new MovementNapi(env, jsthis);
        napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
            [](napi_env env, void *data, void *hint) {
                (void)env;
                (void)hint;
                MovementNapi *movement = (MovementNapi *)data;
                delete movement;
            },
            nullptr, &(obj->callbackRef_));
        objectMap_.insert(std::pair<int32_t, MovementNapi*>(type, obj));
    }

    if (!obj->On(type, args[ARG_1], false)) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "type: %{public}d already exists", type);
        return result;
    }

    sptr<ImovementCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            break;
        }
    }
    if (!isCallbackExists) {
        callback = new MovementCallback();
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Didn't find callback, created it: %{public}p", callback.GetRefPtr());
        g_MovementClient.SubscribeCallback(MovementDataUtils::MovementType(type), callback);
        callbackMap_.insert(std::pair<int32_t, sptr<ImovementCallback>>(type, callback));
    } else {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "Callback exists.");
        return result;
    }
    InvokeCallBack(env, args, 0);
    napi_get_undefined(env, &result);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value MovementNapi::UnSubscribeMovement(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = { 0 };
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "valueType1: %{public}d", valueType1);
    NAPI_ASSERT(env, valueType1 == napi_string, "type mismatch for parameter 1");
    
    size_t len;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    std::vector<char> typeBuf(len + 1);
    status = napi_get_value_string_utf8(env, args[0], typeBuf.data(), len + 1, &len);
    if (status != napi_ok) {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Failed to get string item");
        return result;
    }

    int32_t type = ConvertTypeToInt(typeBuf.data());
    NAPI_ASSERT(env, type >= MovementDataUtils::MovementType::TYPE_IN_VEHICLE && type <=
        MovementDataUtils::MovementType::TYPE_HIGH_SPEED_RAIL, "type is illegal");

    if (argc != 1) {
        napi_valuetype valueType2 = napi_undefined;
        napi_typeof(env, args[ARG_1], &valueType2);
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "valueType2: %{public}d", valueType2);
        NAPI_ASSERT(env, valueType2 == napi_function, "type mismatch for parameter 2");
    }

    MovementNapi* obj = nullptr;
    bool isObjExists = false;
    for (auto it = objectMap_.begin(); it != objectMap_.end(); ++it) {
        if (it->first == type) {
            isObjExists = true;
            obj = (MovementNapi*)(it->second);
            MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Found object");
        }
    }
    if (!isObjExists) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "Didn't find object, so created it");
        return result;
    }
    if (!obj->Off(type, false)) {
        NAPI_ASSERT(env, false, "Failed to get callback for type");
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Failed to get callback for type: %{public}d", type);
        return result;
    } else {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "erase objectMap_");
        objectMap_.erase(type);
    }

    sptr<ImovementCallback> callback;
    bool isCallbackExists = false;
    for (auto it = callbackMap_.begin(); it != callbackMap_.end(); ++it) {
        if (it->first == type) {
            isCallbackExists = true;
            callback = (sptr<ImovementCallback>)(it->second);
            MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Found callback: %{public}p", callback.GetRefPtr());
            break;
        }
    }
    if (!isCallbackExists) {
        NAPI_ASSERT(env, false, "No existed callback");
        return result;
    } else if (callback != nullptr) {
        g_MovementClient.UnSubscribeCallback(MovementDataUtils::MovementType(type), callback);
        callbackMap_.erase(type);
        if (argc == ARG_2) {
            InvokeCallBack(env, args, 0);
        }
    }
    napi_get_undefined(env, &result);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value MovementNapi::GetMovement(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    size_t argc = ARG_2;
    napi_value args[ARG_2] = {0};
    napi_value jsthis;
    void *data = nullptr;

    napi_status status = napi_get_cb_info(env, info, &argc, args, &jsthis, &data);
    NAPI_ASSERT(env, (status == napi_ok) && (argc >= 0), "Bad parameters");

    napi_valuetype valueType1 = napi_undefined;
    napi_typeof(env, args[ARG_0], &valueType1);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "valueType1: %{public}d", valueType1);
    NAPI_ASSERT(env, valueType1 == napi_string, "type mismatch for parameter 1");

    napi_valuetype valueType2 = napi_undefined;
    napi_typeof(env, args[ARG_1], &valueType2);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "valueType2: %{public}d", valueType2);
    NAPI_ASSERT(env, valueType2 == napi_function, "type mismatch for parameter 2");

    size_t len;
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
    std::vector<char> typeBuf(len + 1);
    status = napi_get_value_string_utf8(env, args[0], typeBuf.data(), len + 1, &len);
    if (status != napi_ok) {
        MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Failed to get string item");
        return result;
    }

    int32_t type = ConvertTypeToInt(typeBuf.data());
    NAPI_ASSERT(env, type >= MovementDataUtils::MovementType::TYPE_IN_VEHICLE && type <=
        MovementDataUtils::MovementType::TYPE_HIGH_SPEED_RAIL, "type is illegal");

    MovementNapi* obj = new MovementNapi(env, jsthis);
    napi_wrap(env, jsthis, reinterpret_cast<void *>(obj),
        [](napi_env env, void *data, void *hint) {
            (void)env;
            (void)hint;
            MovementNapi *movement = (MovementNapi *)data;
            delete movement;
        },
        nullptr, &(obj->callbackRef_));

    if (!obj->On(type, args[1], true)) {
        MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "type: %{public}d already exists", type);
        return result;
    }

    MovementDataUtils::MovementData movementData = \
        g_MovementClient.GetMovementData(MovementDataUtils::MovementType(type));

    obj->OnMovementChangedDone(movementData.type, movementData.value, true);
    obj->Off(movementData.type, true);

    napi_get_undefined(env, &result);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return result;
}

napi_value MovementNapi::EnumMovementTypeConstructor(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value MovementNapi::CreateEnumMovementType(napi_env env, napi_value exports)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value vehicle = nullptr;
    napi_value automobile = nullptr;
    napi_value plane = nullptr;
    napi_value metro = nullptr;
    napi_value elevator = nullptr;
    napi_value bicyle = nullptr;
    napi_value stay = nullptr;
    napi_value still = nullptr;
    napi_value relativeStill = nullptr;
    napi_value onFoot = nullptr;
    napi_value walking = nullptr;
    napi_value walkingHandhold = nullptr;
    napi_value fastWalking = nullptr;
    napi_value running = nullptr;
    napi_value lyingPosture = nullptr;
    napi_value smartFlight = nullptr;
    napi_value highSpeedRail = nullptr;

    napi_create_string_utf8(env, "inVehicle", NAPI_AUTO_LENGTH, &vehicle);
    napi_create_string_utf8(env, "inAuto", NAPI_AUTO_LENGTH, &automobile);
    napi_create_string_utf8(env, "inPlane",  NAPI_AUTO_LENGTH, &plane);
    napi_create_string_utf8(env, "inMetro", NAPI_AUTO_LENGTH, &metro);
    napi_create_string_utf8(env, "inElevator", NAPI_AUTO_LENGTH, &elevator);
    napi_create_string_utf8(env, "inBicycle",  NAPI_AUTO_LENGTH, &bicyle);
    napi_create_string_utf8(env, "stay", NAPI_AUTO_LENGTH, &stay);
    napi_create_string_utf8(env, "still", NAPI_AUTO_LENGTH, &still);
    napi_create_string_utf8(env, "relativeStill",  NAPI_AUTO_LENGTH, &relativeStill);
    napi_create_string_utf8(env, "walking", NAPI_AUTO_LENGTH, &walking);
    napi_create_string_utf8(env, "walkingHandHold", NAPI_AUTO_LENGTH, &walkingHandhold);
    napi_create_string_utf8(env, "fastWalking",  NAPI_AUTO_LENGTH, &fastWalking);
    napi_create_string_utf8(env, "running", NAPI_AUTO_LENGTH, &running);
    napi_create_string_utf8(env, "lyingPosture", NAPI_AUTO_LENGTH, &lyingPosture);
    napi_create_string_utf8(env, "fastWalking",  NAPI_AUTO_LENGTH, &highSpeedRail);

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_IN_VEHICLE", vehicle),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_IN_AUTO", automobile),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_IN_PLANE", plane),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_IN_METRO", metro),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_IN_ELEVATOR", elevator),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_ON_BICYCLE", bicyle),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_STAY", stay),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_STILL", still),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_RELATIVE_STILL", relativeStill),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_ON_FOOT", onFoot),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_WALKING", walking),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_WALKING_HANDHOLD", walkingHandhold),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_FAST_WALKING", fastWalking),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_RUNNING", running),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_LYING_POSTURE", lyingPosture),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_SMART_FLIGHT", smartFlight),
        DECLARE_NAPI_STATIC_PROPERTY("TYPE_HIGH_SPEED_RAIL", highSpeedRail),
    };
    napi_value result = nullptr;
    napi_define_class(env, "MovementType", NAPI_AUTO_LENGTH, EnumMovementTypeConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_set_named_property(env, exports, "MovementType", result);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_value MovementNapi::EnumMovementValueConstructor(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value thisArg = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return thisArg;
}

napi_value MovementNapi::ResponseConstructor(napi_env env, napi_callback_info info)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value thisVar = nullptr;
    void *data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, &data);

    auto entity = new ResponseEntity();
    napi_wrap(
        env, thisVar, entity,
        [](napi_env env, void *data, void *hint) {
            MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Destructor");
            auto entity = (ResponseEntity*)data;
            delete entity;
        },
        nullptr, nullptr);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");

    return thisVar;
}

napi_status MovementNapi::AddProperty(napi_env env, napi_value object, const std::string name, int32_t enumValue)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_status status;
    napi_value enumNapiValue;

    status = napi_create_int32(env, enumValue, &enumNapiValue);
    if (status == napi_ok) {
        status = napi_set_named_property(env, object, name.c_str(), enumNapiValue);
    }
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return status;
}

napi_value MovementNapi::CreateMovementValueObject(napi_env env)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_value result = nullptr;
    napi_status status;
    std::string propName;

    status = napi_create_object(env, &result);
    if (status == napi_ok) {
        for (unsigned int i = MovementDataUtils::MovementValue::VALUE_ENTER; i < vecMovementValue.size(); i++) {
            propName = vecMovementValue[i];
            MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "propName: %{public}s", propName.c_str());
            status = AddProperty(env, result, propName, i);
            if (status != napi_ok) {
                MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "Failed to add named prop!");
                break;
            }
            propName.clear();
        }
    }
    if (status == napi_ok) {
        // The reference count is for creation of movement value Object Reference
        status = napi_create_reference(env, result, 1, &movementValueRef_);
        if (status == napi_ok) {
            return result;
        }
    }
    MOVEMENT_HILOGE(MOVEMENT_MODULE_JS_NAPI, "CreateMovementValueObject is Failed!");
    napi_get_undefined(env, &result);

    return result;
}

napi_value MovementNapi::CreateResponseClass(napi_env env, napi_value exports)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");

    napi_property_descriptor desc[] = {
        DECLARE_NAPI_PROPERTY("movementValue", CreateMovementValueObject(env)),
    };

    napi_value result = nullptr;
    napi_define_class(env, "Response", NAPI_AUTO_LENGTH, ResponseConstructor, nullptr,
        sizeof(desc) / sizeof(*desc), desc, &result);

    napi_create_reference(env, result, 1, &g_responseConstructor);
    napi_set_named_property(env, exports, "Response", result);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return exports;
}

napi_value MovementNapi::Init(napi_env env, napi_value exports)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("on", SubscribeMovement),
        DECLARE_NAPI_FUNCTION("off", UnSubscribeMovement),
        DECLARE_NAPI_FUNCTION("once", GetMovement),
    };
    NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));

    CreateEnumMovementType(env, exports);
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");
    return exports;
}

EXTERN_C_START
/*
 * function for module exports
 */
static napi_value MovementInit(napi_env env, napi_value exports)
{
    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Enter");

    napi_value ret = MovementNapi::Init(env, exports);

    MOVEMENT_HILOGD(MOVEMENT_MODULE_JS_NAPI, "Exit");

    return ret;
}
EXTERN_C_END

/*
 * Module definition
 */
static napi_module g_module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = "movement",
    .nm_register_func = MovementInit,
    .nm_modname = "movement",
    .nm_priv = ((void *)0),
    .reserved = {0}
};

/*
 * Module registration
 */
extern "C" __attribute__((constructor)) void RegisterModule(void)
{
    napi_module_register(&g_module);
}
